



from src.common import RemoteContentFetcher;
class BasicRemoteContentFetcher(RemoteContentFetcher):
	def __init__(self):
		self._requests = [];


	def fetchRequest(self, request):
		import urllib;
		from lib import Config;
		
		from lib import Logger;
		#Logger.quickLog("BasicRemoteContentFetcher - fetchRequest: function starts", Logger.DEBUG);

		url = request.getUrl();
		proxy = Config.get('proxy');
		
		#form_data = urllib.urlencode(form_fields)
		
		if (len(proxy) > 0):
			url = url + ':' + proxy;

		#Logger.quickLog("BasicRemoteContentFetcher - fetchRequest: URL " + url, Logger.DEBUG);

		outHeaders = {};
		if (request.hasHeaders()):
			headers = request.getHeaders().split("\n");
			for header in headers:
				if (header.find(':') != -1):
					
					key = header[0:header.find(':')].strip();
					val = header[(header.find(':') + 1):].strip();

					#Logger.quickLog("BasicRemoteContentFetcher - fetchRequest: header key - " + key + ", value - " + value, Logger.DEBUG);

					if (key.lower() != "transfer-encoding" and key.lower() != "cache-control" and key.lower() != "expires"  and key.lower() != "Content-Length"):
						outHeaders[key] = val;

		
		#Logger.quickLog("BasicRemoteContentFetcher - fetchRequest: outHeader - " + ','.join(outHeaders.keys()), Logger.DEBUG);
	
		form_fields = [];
		url = url.encode("utf-8") 
		
		#Logger.quickLog("BasicRemoteContentFetcher - fetchRequest: url " + url, Logger.DEBUG);

		if (request.isPost()):

			#Logger.quickLog("BasicRemoteContentFetcher - fetchRequest: post resuest", Logger.DEBUG);
			from google.appengine.api import urlfetch;
			
			form_fields = request.getPostBody();
			if(form_fields <> None):
				import urllib;
				result = urlfetch.fetch(url=url,payload=urllib.urlencode(form_fields),method=urlfetch.POST,headers=outHeaders);
			else:
				result = urlfetch.fetch(url, method=urlfetch.GET, headers=outHeaders);
				
		else:
			#Logger.quickLog("BasicRemoteContentFetcher - fetchRequest: get resuest", Logger.DEBUG);
			from google.appengine.api import urlfetch;

			result = urlfetch.fetch(url=url, method=urlfetch.GET, headers=outHeaders)


		#header = '';
		#body = '';	
		
		#  on redirects and such we get multiple headers back from curl it seems, we really only want the last one
		#while (result.content[0:4] == 'HTTP' and result.content.find('\r\n\r\n') != -1):
		#	header = result.content[0:result.content.find('\r\n\r\n')];
		#	content = body = result.content[(len(header) + 4):];

		httpCode = result.status_code;
		if (not httpCode):
			httpCode = 404;

		#Logger.quickLog("BasicRemoteContentFetcher - fetchRequest: url fetched by net call", Logger.DEBUG);

		request.setHttpCode(httpCode);
		request.setContentType(result.headers['content-type']);
		request.setResponseHeaders(result.headers);

		# fix, some times garbage chars come prepended to xml
		content = result.content;
		#Logger.quickLog("BasicRemoteContentFetcher - content : ", Logger.DEBUG);
		
		pos = content.find('<?xml version=');

		from lib import Util;
		content = Util.substr(content, pos);		

		request.setResponseContent(content);
		request.setResponseSize(len(content));
		return request;



from src.common import RemoteContent, RemoteContentRequest;
from src.common.sample import BasicRemoteContentFetcher;

class BasicRemoteContent(RemoteContent):
	def fetch(self, request, context):
		
		from lib import Config;
		cache = Config.get('data_cache');
		cache = cache();



		from lib import Logger;

		#Logger.quickLog("BasicRemoteContent - fetch function starts", Logger.DEBUG);

		remoteContentFetcher = BasicRemoteContentFetcher();
		if (not (isinstance(request, RemoteContentRequest))):
			raise lib.CommonException("Invalid request type in remoteContent");

		#Logger.quickLog("BasicRemoteContent - fetch step 1", Logger.DEBUG);
		
		# determine which requests we can load from cache, and which we have to actually fetch
		if (Config.get('cache_objects') and not context.getIgnoreCache() and not request.isPost()):
			cachedRequest = cache.get(request.toHash(), context.getRefreshInterval());

			if (cachedRequest  != False):
				return cachedRequest;
		
		#Logger.quickLog("BasicRemoteContent - fetch step 2", Logger.DEBUG);


		ret = remoteContentFetcher.fetchRequest(request);
		#Logger.quickLog("BasicRemoteContent - fetch step 3", Logger.DEBUG);
		# only cache requests that returned a 200 OK

		if (request.getHttpCode() == '200' and Config.get('cache_objects')):
			pass;
			#Logger.quickLog("BasicRemoteContent - fetch: setting cache for successful request fetch", Logger.DEBUG);
			#Logger.quickLog("BasicRemoteContent - fetch: hash for request" + request.toHash(), Logger.DEBUG);
			
#			cache.set(request.toHash(), request);
			


		
		
		#import sys;
		#sys.exit();

		#Logger.quickLog("BasicRemoteContent - fetch step 4", Logger.DEBUG);
		
		
		return ret;




# Primitive token implementation that uses stings as tokens.
from src.common import SecurityToken;
class BasicSecurityToken(SecurityToken):

	def __init__(self, token, maxAge, owner, viewer, app, domain, appUrl, moduleId):
		self.__OWNER_KEY = "o";
		self.__APP_KEY = "a";
		self.__VIEWER_KEY = "v";
		self.__DOMAIN_KEY = "d";
		self.__APPURL_KEY = "u";
		self.__MODULE_KEY = "m";

		self.__crypter = self._getCrypter();
		if (token != None and len(token) > 0):
			self.__token = token;
			self.__tokenData = self.__crypter.unwrap(token, maxAge);
		else:
			self.__tokenData = {};
			self.__tokenData[self.__OWNER_KEY] = owner;
			self.__tokenData[self.__VIEWER_KEY] = viewer;
			self.__tokenData[self.__APP_KEY] = app;
			self.__tokenData[self.__DOMAIN_KEY] = domain;
			self.__tokenData[self.__APPURL_KEY] = appUrl;
			self.__tokenData[self.__MODULE_KEY] = moduleId;
			self.__token = self.__crypter.wrap(self.__tokenData);
	

	def toSerialForm(self):
		import  urllib;
		return urllib.quote(self.__token);

	'''
	 * Generates a token from an input string
	 * @param token String form of token
	 * @param maxAge max age of the token (in seconds)
	 * @throws BlobCrypterException 
	 '''
	@staticmethod
	def createFromToken(token, maxAge):
		return BasicSecurityToken(token, maxAge, None, None, None, None, None, None);

	'''
	 * Generates a token from an input array of values
	 * @param owner owner of this gadget
	 * @param viewer viewer of this gadget
	 * @param app application id
	 * @param domain domain of the container
	 * @param appUrl url where the application lives
	 * @param moduleId module id of this gadget 
	 * @throws BlobCrypterException 
	 '''
	@staticmethod
	def createFromValues(owner, viewer, app, domain, appUrl, moduleId):
		return BasicSecurityToken(None, None, owner, viewer, app, domain, appUrl, moduleId);

	
	def _getCrypter(self):
		return BasicBlobCrypter();

	def isAnonymous(self):
		return (self.__tokenData[self.__OWNER_KEY] == 0 and self.__tokenData[self.__VIEWER_KEY] == 0);
	

	def getAppId(self):
		if (self.isAnonymous()):
			raise Exception, "Can't get appId from an anonymous token";
		
		return self.__tokenData[self.__APP_KEY];

	def getDomain(self):
		if (self.isAnonymous()):
			raise Exception, "Can't get domain from an anonymous token";
		
		return self.__tokenData[self.__DOMAIN_KEY];
	
	def getOwnerId(self):
		if (self.isAnonymous()):
			raise Exception, "Can't get ownerId from an anonymous token";
		
		return self.__tokenData[self.__OWNER_KEY];
	

	
	def getViewerId(self):
		if (self.isAnonymous()):
			raise Exception, "Can't get viewerId from an anonymous token";
		
		return self.__tokenData[self.__VIEWER_KEY];

	def getAppUrl(self):
		if (self.isAnonymous()):
			raise Exception, "Can't get appUrl from an anonymous token";
		
		return self.__tokenData[self.__APPURL_KEY];
	

	def getModuleId(self):
		if (self.isAnonymous()):
			raise Exception, "Can't get moduleId from an anonymous token";
		
		#if (!is_numeric(self.tokenData[self.MODULE_KEY])):
		#	raise Exception("Module ID should be an integer");
		
		return self.__tokenData[self.__MODULE_KEY];


from src.common import SecurityTokenDecoder;
class BasicSecurityTokenDecoder(SecurityTokenDecoder):
	def __init__(self):
		self.__OWNER_INDEX = 0;
		self.__VIEWER_INDEX = 1;
		self.__APP_ID_INDEX = 2;
		self.__CONTAINER_INDEX = 3;
		self.__APP_URL_INDEX = 4;
		self.__MODULE_ID_INDEX = 5;

	
	# @inheritDoc
	
	# Returns a token with some faked out values.
	
	def createToken(self, stringToken):
		from lib import RequestUtil;
		requestUtilHandler = RequestUtil.getInstance();
		from lib import Config;

		if (len(stringToken) == 0 and (requestUtilHandler.GETORPOST.has_key('authz') and len(requestUtilHandler.GETORPOST.has_key['authz']) > 0)):
			from src.gadgets.servlet import GadgetException;
			raise GadgetException('INVALID_GADGET_TOKEN');
		
		try:
		#if True:

			# TODO remove this once we have a better way to generate a fake token
			#  in the example files
			
			tokens = stringToken.split(":");
			import  urllib;
			if (Config.get('allow_plaintext_token') and len(tokens) == 6):
				return BasicSecurityToken(None, None, urllib.unquote(tokens[self.__OWNER_INDEX]), urllib.unquote(tokens[self.__VIEWER_INDEX]), urllib.unquote(tokens[self.__APP_ID_INDEX]), urllib.unquote(tokens[self.__CONTAINER_INDEX]), urllib.unquote(tokens[self.__APP_URL_INDEX]), urllib.unquote(tokens[self.__MODULE_ID_INDEX]));
			else:
				return BasicSecurityToken.createFromToken(stringToken, Config.get('token_max_age'));
			
		
		except:
			from src.gadgets.servlet import GadgetException;
			raise GadgetException, 'INVALID_GADGET_TOKEN';



class BlobExpiredException (Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)


# This class provides basic binary blob encryption and decryption, for use with the security token

from src.common import BlobCrypter;
class BasicBlobCrypter(BlobCrypter):
	# FIXME make this compatible with the javas blobcrypter
	

	# Labels for key derivation
	def __init__(self):
		self.__CIPHER_KEY_LABEL = 0;
		self.__HMAC_KEY_LABEL = 1;
	
		# Key used for time stamp (in seconds) of data
		self.TIMESTAMP_KEY = 't';
	
		# minimum length of master key
		self.MASTER_KEY_MIN_LEN = 16;
	
		# allow three minutes for clock skew
		self.__CLOCK_SKEW_ALLOWANCE = 180;
	
		self.__UTF8 = "UTF-8";
		from lib import Config;
	
		self.__cipherKey = Config.get('token_cipher_key');
		self.__hmacKey = Config.get('token_hmac_key');


	def wrap(self, ina):
		encoded = self.__serializeAndTimestamp_(ina);
		import base64;
		from lib import Config;
		if (Config.get('allow_plaintext_token')):
			cipherText = base64.encodestring(encoded);
		else:
			cipherText = Crypto.aes128cbcEncrypt(self.__cipherKey, encoded);
		from src.common import Crypto;
		crypto = Crypto();
		hmac_val = crypto.hmacSha1(self.__hmacKey, cipherText);
		b64 = base64.encodestring(cipherText + hmac_val);
		return b64;

	def __serializeAndTimestamp_(self, ina):
		encoded = "";
		import urllib;
		for key in ina:
			if(type(ina[key]) == 'string'):
				encoded = encoded + urllib.urlencode(key) + '=' + urllib.urlencode(str(ina[key])) + "&";
			else:
				encoded = encoded + key + '=' + ina[key] + "&";
		import time;
		encoded = encoded + self.TIMESTAMP_KEY + '=' + str(time.time());
		return encoded;

	def unwrap(self, ina, maxAgeSec):
		#TODO remove this once we have a better way to generate a fake token in the example files
		from lib import Config;
		if (Config.get('allow_plaintext_token') and len(ina.split(':')) == 6):
			data = ina.split(':');
			out['o'] = data[0];
			out['v'] = data[1];
			out['a'] = data[2];
			out['d'] = data[3];
			out['u'] = data[4];
			out['m'] = data[5];
		else:
			import base64;
			from src.common import Crypto;
			objCrypto = Crypto();
			bin = base64.decodestring(ina);
			cipherText = bin[0:(len(bin) - objCrypto.HMAC_SHA1_LEN)];
			hmac = bin[0:len(cipherText)];
			objCrypto.hmacSha1Verify(self.__hmacKey, cipherText, hmac);
			if (Config.get('allow_plaintext_token')):
				plain = base64.decode(cipherText);
			else:
				plain = objCrypto.aes128cbcDecrypt(self.__cipherKey, cipherText);
			out = self.__deserialize_(plain);
			self.__checkTimestamp_(out, maxAgeSec);
		return out;

	def __deserialize_(self, plain):
		items = plain.split('[&=]', plain);
		import urllib;
		i = 0;
		while i < items.length:
			key = urllib.decode(items[i]);
			i = i + 1;
			value = urllib.decode(items[i]);
			i = i + 1;
			map[key] = value;
		return map;

	def __checkTimestamp_(out, maxAge):
		import time;
		minTime = out[self.TIMESTAMP_KEY] - self.__CLOCK_SKEW_ALLOWANCE;
		maxTime = out[self.TIMESTAMP_KEY] + maxAge + self.__CLOCK_SKEW_ALLOWANCE;
		now = time.time();
		if ((minTime < now and now < maxTime) == False):
			raise BlobExpiredException("Security token expired");